import React from "react";
import {Form} from "antd";
import {get, indexOf, isEmpty, isEqual, isFunction, omit} from "lodash-es";
import BaseComp from "../Base/BaseComp";
import PropTypes from 'prop-types';
import {getRandomKey, isNotEmpty, isObjectValEqual} from "jh-utils";
import {ComponentType, FieldCtrl} from "../Config";

const EditableContext = React.createContext();

export class EditableRow extends BaseComp {

    static propTypes = {
        record: PropTypes.object,
        className: PropTypes.any,
        /**
         * @param e
         * @param record
         * @return void
         */
        onClick: PropTypes.func
    };

    formRef = React.createRef();

    state = {
        focus: false,
    };

    componentDidUpdate(prevProps, prevState) {

        if (!isObjectValEqual(prevProps.record, this.props.record)) {
            if (this.formRef.current && this.formRef.current.setFieldsValue) {
                this.formRef.current.setFieldsValue(this.props.record);
            }
        }
    }


    setFocus(value, afterCb = null) {
        if (this.state.focus !== value) {
            this.setState({focus: value}, afterCb);
        }
    }

    mainRender(context) {
        const {
            record,
            className,
            onClick,
            onDoubleClick,
            onRef,
            ...restProps
        } = this.props;
        const {focus} = this.state;
        const classNameVal = `${className || ''} ${focus ? 'active' : ''}`;

        return (
            <Form component={false}
                  ref={this.formRef}
                  initialValues={record}>
                <EditableContext.Provider value={{
                    rowIns: this
                }}>
                    <tr {...restProps}
                        className={classNameVal}
                        onClick={(e) => {
                            this.setFocus(true);
                            isFunction(onClick) && onClick(e, record);
                        }}
                        onDoubleClick={(e) => {
                            isFunction(onDoubleClick) && onDoubleClick(e, record);
                        }}
                    />
                </EditableContext.Provider>
            </Form>
        );
    }
}

export class EditableCell extends BaseComp {

    static propTypes = {
        title: PropTypes.node,
        editable: PropTypes.bool,
        children: PropTypes.node,
        dataIndex: PropTypes.string,
        rowIndex: PropTypes.number,
        /**
         *
         * @param value
         * @param record
         * @param rowInd
         * @return any
         */
        renderFunc: PropTypes.func,
        record: PropTypes.object,
        /**
         *
         * @param value
         * @param record
         * @param rowIndex
         * @return {*}
         */
        editingSetting: PropTypes.func,
        /**
         *
         * @param value
         * @param dataIndex
         * @param rowIndex
         * @param oldRecord
         * @param afterCB
         * @return {*}
         */
        onSave: PropTypes.func,
        scroll2View: PropTypes.func
    };

    static contextType = EditableContext;

    static getDerivedStateFromProps(nextProps, prevState) {

        const {record, dataIndex} = nextProps;
        let dataIndexValue = record ? get(record, dataIndex) : undefined;

        // if (!isEqual(dataIndexValue, prevState.propValue)) {
        if (!isObjectValEqual(record, prevState.propRecord)) {
            return {
                value: dataIndexValue,
                propValue: dataIndexValue,
                propRecord: record
            }
        }
        return null;
    }

    initialValue;

    constructor(props) {
        super(props);
        const {record, dataIndex} = props;
        let stateValue = record ? get(record, dataIndex) : undefined;

        this.state = {
            editing: false,
            value: stateValue,
            propValue: stateValue,
            propRecord: record
        };
    }

    setFocus(editing, afterCB) {
        // console.log('setFocus')

        if (this.state.editing !== editing) {
            const moreState = {};
            if (editing) {
                moreState.focusing = true;
                const {dataIndex} = this.props;
                const {rowIns} = this.context;
                let form;

                if (rowIns && rowIns.formRef && rowIns.formRef.current) {
                    form = rowIns.formRef.current;
                }

                if (form) {
                    form.setFieldsValue({
                        [dataIndex]: this.state.value
                    });
                    this.setState({editing, ...moreState}, afterCB);
                } else {
                    this.reRender(() => {
                        const {rowIns} = this.context;
                        let form;

                        if (rowIns && rowIns.formRef && rowIns.formRef.current) {
                            form = rowIns.formRef.current;
                        }
                        form && form.setFieldsValue({
                            [dataIndex]: this.state.value
                        });
                        this.setState({editing, ...moreState}, afterCB);
                    })
                }

                if (this.props.scroll2View) {
                    this.props.scroll2View(this.tdRef);
                }

            } else {
                this.setState({editing, ...moreState}, afterCB);
            }

        }
    }

    save = (e, afterCB, moreState = {}) => {
        const {
            record,
            onSave,
            dataIndex,
            rowIndex,
        } = this.props;
        const {rowIns} = this.context;
        let form;

        if (rowIns && rowIns.formRef && rowIns.formRef.current) {
            form = rowIns.formRef.current;
        }

        if (form && form.getFieldValue) {
            const value = form.getFieldValue(dataIndex);

            if (!isEqual(value, this.state.value)) {
                let oldValue = this.state.value;

                if (isFunction(onSave)) {
                    let newRecord = onSave(value, dataIndex, rowIndex, record, afterCB);
                    if (false === newRecord) {
                        // 不更新此次修改
                        let obj = {};
                        obj[dataIndex] = oldValue;
                        form.setFieldsValue(obj);

                        this.initialValue = oldValue;
                        this.setState({value: oldValue, ...moreState});
                    } else {
                        let newValue = newRecord[dataIndex];
                        this.initialValue = newValue;
                        this.setState({value: newValue, ...moreState});
                    }
                } else {
                    this.initialValue = value;
                    this.setState({value: value, ...moreState}, afterCB);
                    // isFunction(afterCB) && (afterCB());
                }
            } else {
                if (!isEmpty(moreState)) {
                    this.setState(moreState);
                }
                isFunction(afterCB) && (afterCB());
            }
        }
    };

    getFieldsComp() {
        const {
            record,
            rowIndex,
            dataIndex,
            editingSetting,
            prevFunc,
            nextFunc,
        } = this.props;

        if (!isFunction(editingSetting)) {
            console.error(dataIndex + '列的 editingSetting 不是个函数');
            return null;
        }
        const settingObj = editingSetting(get(record, dataIndex), record, rowIndex, {
            onChange: (value, needSave) => {
                // console.log('editingSetting onChange', value);
                if (false !== needSave) {
                    this.save();
                }
            },
            cell: true, // 标识‘此组件在单元格内’
            record,
            dataIndex,
            prevFunc,
            nextFunc
        });
        const comp = settingObj.comp;
        const options = settingObj.options || {};

        if ('undefined' === typeof comp || null === comp) {
            console.error(dataIndex + '列 editingSetting 缺少 comp');
            return null;
        }

        if ('undefined' !== typeof options.initialValue) {
            this.initialValue = options.initialValue;
        }

        return (
            <Form.Item
                style={{margin: 0}}
                name={dataIndex}
                {...omit(options, ['initialValue'])}
            >
                {comp}
            </Form.Item>
        );
    }

    _isAwaysEdit() {
        let compType = this.props.componentType;
        if (this.props.componentType === ComponentType.GENERAL_COMPONENT) {
            let {componentType} = this.state.value || {};
            if (componentType) compType = componentType;
        }
        return indexOf([
            ComponentType.EMBED_SELECTOR,
            ComponentType.CUST_EMBED_SELECTOR
        ], compType) > -1;
    }

    _showChildren() {
        const {
            children,
            rowIndex,
            renderFunc,
            record,
        } = this.props;

        return isFunction(renderFunc) ? renderFunc(this.state.value, record, rowIndex) : (this.state.value || children)
    }

    mainRender(context) {
        const {
            className,
            title,
            editable,
            children,
            rowIndex,
            renderFunc,
            dataIndex,
            record,
            editingSetting,
            componentType,
            onRef,
            onClick,
            onBlur,
            onSave,
            scroll2View,
            prevFunc,
            nextFunc,
            fieldCtrl,
            ...restProps
        } = this.props;
        let {editing, value} = this.state;
        let classNameVal = `${className || ''} ${editable ? 'active' : ''}`;

        // if(fieldCtrl == FieldCtrl.REQUIRED && !isNotEmpty(value)) {
        //     classNameVal = `${classNameVal} error`
        // }
        let moreProps = {
            className: classNameVal
        };

        if (editable) {
            moreProps.onClick = (e) => {
                e.preventDefault();
                e.stopPropagation();
                // console.log('cell onclick', e);
                this.setFocus(true, () => {
                    isFunction(onClick) && onClick(e, record);
                });
                return false;
            };
            // moreProps.onBlur = (e) => {
            //     console.log('cell onblur');
            //     this.save(null, () => {
            //         isFunction(onBlur) && onBlur(e, record);
            //     }, {editing: false});
            // };
        } else if (isFunction(onClick)) {
            moreProps.onClick = onClick;
        }

        return <td
            ref={(ref) => {
                this.tdRef = ref
            }}
            {...restProps}
            {...moreProps}
        >{
            editable ? (
                (editing || this._isAwaysEdit()) ? (
                    this.getFieldsComp()
                ) : (this._showChildren())
            ) : (
                children
            )
        }</td>;
    }

}